package service

import (
	"github.com/pkg/errors"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/dao"
	"nft-server/logger"
	"nft-server/model"
	"nft-server/util"
	"time"
)

const (
	BurningStatus           = 1
	ToBeBurntStatus         = 0
	QueryTimeIntervalSecond = 5
)

func BurnInit() {
	go func() {
		adminInfo, err := GetAdmin()
		logger.Info("Burn queue Init successfully")
		if err != nil {
			logger.Error("BurnInit Get admin error: %v", err)
			return
		}

		for {
			lock.Lock()
			burnAssetDetail, err := GetOneBurnAsset(ToBeBurntStatus)
			if burnAssetDetail != nil {
				err = UpdateBurnAssetOperatingStatus(burnAssetDetail.Id, BurningStatus)
			}
			lock.Unlock()

			if err != nil {
				logger.Error("Burn error: GetOneBurnAssetError and UpdateBurnAssetOperatingStatus Error error: %v", err)
				time.Sleep(QueryTimeIntervalSecond * time.Second)
				continue
			}
			if burnAssetDetail == nil {
				time.Sleep(QueryTimeIntervalSecond * time.Second)
				continue
			}

			if burnAssetDetail.TokenId == "" { // 销毁集合
				err := burnSetAsset(adminInfo, burnAssetDetail)
				if err != nil {
					logger.Error("Burn error: %v", err)
					time.Sleep(QueryTimeIntervalSecond * time.Second)
					continue
				}
			}
			time.Sleep(QueryTimeIntervalSecond * time.Second)
		}
	}()

}

func InsertBurnAsset(asset *model.BurnAsset) error {
	err := dao.GetBurnAssetDao().InsertBurnAsset(asset)
	if err != nil {
		return err
	}
	return nil
}

func GetOneBurnAsset(operatingStatus int) (*model.BurnAsset, error) {
	burnAssetDetail, err := dao.GetBurnAssetDao().GetOneBurnAsset(operatingStatus)
	if err != nil {
		return nil, err
	}
	return burnAssetDetail, nil
}

func UpdateBurnAssetOperatingStatus(BurnAssetId, operatingStatus int) error {
	err := dao.GetBurnAssetDao().UpdateBurnAssetOperatingStatus(BurnAssetId, operatingStatus)
	if err != nil {
		return err
	}
	return nil
}

func BurnAsset(userId int, adminInfo *model.User, collectionIndex, tokenId string) (txId string, err error) {
	if userId != adminInfo.Id {
		userInfo, err := GetUserAllInfoById(userId)
		if err != nil {
			return "", errors.Errorf("BurnAsset error: fail to get UserInfo by userId: %v", err)
		}
		err = SwitchIdentityWithPem(userInfo.Cert, userInfo.Key)
		if err != nil {
			return "", errors.Errorf("Burn asset error: fail to switch identity with pem to user: %v", err)
		}
		defer SwitchIdentityWithPem(adminInfo.Cert, adminInfo.Key)
	}

	txId, err = sdk.Burn(collectionIndex, tokenId)
	if err != nil {
		return "", errors.Errorf("BurnAsset error: %v", err)
	}
	return txId, nil
}

func DeleteOneBurnAsset(burnAssetDetailId int) error {
	err := dao.GetBurnAssetDao().DelOneBurnAsset(burnAssetDetailId)
	if err != nil {
		return err
	}
	return nil
}

func JudgeAndUpdateStatus(collectionDetail *model.CollectionDetail) (bool, error) {
	if collectionDetail.CollectionStatus != util.CollectionMintCompleted {
		return false, nil
	}
	collectionDetail.CollectionStatus = util.CollectionBurning
	err := UpdateCollectionDetail(collectionDetail)
	if err != nil {
		return false, errors.Errorf("JudgeAndUpdateStatus: UpdateCollectionDetail error: %v", err)
	}
	return true, nil
}

func burnSetAsset(adminInfo *model.User, burnAssetDetail *model.BurnAsset) error {
	assetRelationInfos, err := GetAssetRelationByCollectionId(burnAssetDetail.CollectionIndex, adminInfo.Id, burnAssetDetail.SubCollectionIndex)
	if err != nil {
		return errors.Errorf("burnSetAsset error: %v", err)
	}
	if len(assetRelationInfos) != 0 { //管理员手上含有可销毁的asset
		for _, assetRelationInfo := range assetRelationInfos {
			_, err := BurnAsset(cast.ToInt(burnAssetDetail.Operator), adminInfo, assetRelationInfo.CollectionIndex, assetRelationInfo.TokenId)
			if err != nil {
				return errors.Errorf("burnSetAsset: BurnAsset error: %v", err)
			}
			err = DeleteAsset(assetRelationInfo.CollectionIndex, assetRelationInfo.TokenId)
			if err != nil {
				return errors.Errorf("burnSetAsset: DeleteAsset error: %v", err)
			}
			//删asset_relation
			err = DeleteAssetRelation(assetRelationInfo.CollectionIndex, assetRelationInfo.TokenId, assetRelationInfo.UserId)
			if err != nil {
				return errors.Errorf("burnSetAsset: DeleteAssetRelation error: %v", err)
			}

			lock.Lock()
			err = UpdateCollectionSuppliesAndDeleteWhenZero(adminInfo.Id, burnAssetDetail.CollectionIndex, burnAssetDetail.SubCollectionIndex, -1)
			err = UpdateCollectionSuppliesAndDeleteWhenZero(adminInfo.Id, burnAssetDetail.CollectionIndex, util.DefaultSubCollectionIndex, -1)
			lock.Unlock()

			if err != nil {
				return errors.Errorf("burnSetAsset: UpdateCollectionSupplies error: %v", err)
			}

		}
		err = DeleteOneBurnAsset(burnAssetDetail.Id) //销毁完毕 删除库里burn_asset队列数据
		if err != nil {
			return errors.Errorf("burnSetAsset: DeleteOneBurnAsset error: %v", err)
		}
	} else { //本身手上就没有相关asset, 还原状态
		err = DeleteOneBurnAsset(burnAssetDetail.Id)
		if err != nil {
			return errors.Errorf("burnSetAsset: DeleteOneBurnAsset error: %v", err)
		}
		//找出collectionDetail 还原状态
		err = recoverCollectionStatus(burnAssetDetail)
		if err != nil {
			return errors.Errorf("burnSetAsset: recoverCollectionStatus error: %v", err)
		}
	}

	return nil
}

func recoverCollectionStatus(burnAssetDetail *model.BurnAsset) error {
	//找出collectionDetail 还原状态
	subCollectionDetail, err := dao.GetCollectionDetailDao().GetCollectionDetailByUserIdAndCollection(util.InvalidNumber, burnAssetDetail.CollectionIndex, burnAssetDetail.SubCollectionIndex)
	if err != nil {
		return errors.Errorf("GetCollectionDetailById error: %v", err)
	}

	if subCollectionDetail != nil {
		subCollectionDetail.CollectionStatus = util.CollectionMintCompleted
		err := UpdateCollectionDetail(subCollectionDetail)
		if err != nil {
			return errors.Errorf("UpdateCollectionDetail error: %v", err)
		}
		//判断BurnAsset表中是否存在同一collection Index的表，若无则把collection detail中的母集合也改回铸造完成
		queryBurnAssetParam := common.QueryBurnAssetInfo{
			CollectionIndex: burnAssetDetail.CollectionIndex,
		}
		burnAssetInfos, err := dao.GetBurnAssetDao().GetBurnAssetByCollectionId(&queryBurnAssetParam)
		if err != nil {
			return errors.Errorf("GetBurnAssetByCollectionId error: %v", err)
		}
		if len(burnAssetInfos) == 0 { //BurnAsset表中不存在同一collectionIndex的asset，把母集合也置回到铸造完成状态
			collectionDetail, err := dao.GetCollectionDetailDao().GetCollectionDetailByUserIdAndCollection(util.InvalidNumber, burnAssetDetail.CollectionIndex, util.DefaultSubCollectionIndex)
			if err != nil {
				return errors.Errorf("GetCollectionDetailByUserIdAndCollection error: %v", err)
			}
			if collectionDetail != nil {
				collectionDetail.CollectionStatus = util.CollectionMintCompleted
				err := UpdateCollectionDetail(collectionDetail)
				if err != nil {
					return errors.Errorf("UpdateCollectionDetail error: %v", err)
				}
			}
		}
	}
	return nil
}

func GetBurnAssetByCollectionId(collectionId string, subCollectionId int) ([]*model.BurnAsset, error) {
	var subCollectionIdPointer *int
	if subCollectionId != util.DefaultSubCollectionIndex {
		*subCollectionIdPointer = subCollectionId
	}
	queryBurnAssetParameters := common.QueryBurnAssetInfo{
		CollectionIndex:    collectionId,
		SubCollectionIndex: subCollectionIdPointer,
	}
	burnAssetInfos, err := dao.GetBurnAssetDao().GetBurnAssetByCollectionId(&queryBurnAssetParameters)
	if err != nil {
		return nil, errors.Errorf("GetBurnAssetByCollectionId error: %v", err)
	}
	return burnAssetInfos, nil
}
